package cn.wizzer.app.user.modules.service.impl;

import cn.wizzer.app.user.modules.models.AccountRecordStatistics;
import cn.wizzer.app.user.modules.models.enums.BizTypeEnum;
import cn.wizzer.app.user.modules.models.vo.AccountRecordVo;
import cn.wizzer.app.user.modules.services.UserService;
import cn.wizzer.app.user.modules.services.account.UserAccountRecordService;
import cn.wizzer.app.user.modules.services.account.AccountRecordStatisticsService;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import cn.wizzer.framework.util.Result;
import com.alibaba.dubbo.config.annotation.Service;
import io.jsonwebtoken.lang.Collections;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zl
 * @date 2020/6/17 9:59
 */
@IocBean(args = {"refer:dao"})
@Service(interfaceClass = AccountRecordStatisticsService.class)
public class AccountRecordStatisticsServiceImpl extends BaseServiceImpl<AccountRecordStatistics> implements AccountRecordStatisticsService {
    public AccountRecordStatisticsServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private UserAccountRecordService userAccountRecordService;
    @Inject
    private UserService userService;
    @Inject
    private CommonDBService dbService;

    /**
     * 不统计的流水类型集合
     */
    private static final List TYPE_LIST = Result.createList(BizTypeEnum.INVEST_RESETTLE_DEDUCT, BizTypeEnum.RESETTLE_PRINCIPAL_AND_INCOME,
            BizTypeEnum.EXCHANGE_OUT, BizTypeEnum.EXCHANGE_IN, BizTypeEnum.PURCHASE, BizTypeEnum.PURCHASE_BACK);

    @Override
    public void statistics(Long startTime, Long endTime) {
        // 根据时间参数查询流水记录表
        Sql sql = Sqls.create("SELECT uar.*,sc.rate FROM user_account_record AS uar")
                .appendSourceSql("LEFT JOIN user AS u ON uar.userId = u.id")
                .appendSourceSql("LEFT JOIN sys_currency AS sc ON sc.id = uar.currencyId")
                .appendSourceSql("WHERE bizType NOT IN (@typeList) AND uar.ctAt BETWEEN @startTime AND @endTime AND u.inside = 0")
                .setParam("typeList", TYPE_LIST)
                .setParam("startTime", startTime)
                .setParam("endTime", endTime);
        List<AccountRecordVo> list = dbService.list(AccountRecordVo.class, sql.toString());
        if (!Collections.isEmpty(list)) {
            AccountRecordStatistics statistics = new AccountRecordStatistics();
            // 处理提现
            this.handleWithdraw(statistics, list);
            // 处理充值
            this.handleRecharge(statistics, list);
            // 处理下级返佣
            this.handleSubRebate(statistics, list);
            // 处理投资
            this.handleInvest(statistics, list);
            // 处理红包
            this.handleRedPacket(statistics, list);
            // 签到金币兑换
            this.handleCoinExchange(statistics, list);


            // 登录人数
            int loginNum = userService.count(Cnd.where("visitor", "=", 0).and("inside", "=", 0)
                    .and("lastLoginAt", ">=", startTime / 1000)
                    .and("lastLoginAt", "<", endTime / 1000));
            statistics.setLoginNum(loginNum);
            // 注册人数（不算游客）
            int registerNum = userService.count(Cnd.where("visitor", "=", 0).and("inside", "=", 0)
                    .and("ctAt", ">=", startTime / 1000)
                    .and("ctAt", "<", endTime / 1000));
            statistics.setRegisterNum(registerNum);

            // 统计数据入库
            String date = Times.format("yyyy-MM-dd", Times.D(startTime));
            AccountRecordStatistics fetch = this.fetch(Cnd.where("date", "=", date));
            // 数据已存在则更新，不存在则插入
            if (null != fetch) {
                statistics.setId(fetch.getId());
                statistics.setCtAt(fetch.getCtAt());
                // 每日登录人数不用update
                statistics.setLoginNum(fetch.getLoginNum());
                statistics.setDelFlag(fetch.getDelFlag());
            }
            statistics.setDate(date);
            this.insertOrUpdate(statistics);
        }
    }


    /**
     * 签到金币兑换处理
     *
     * @param statistics
     * @param list
     */
    private void handleCoinExchange(AccountRecordStatistics statistics, List<AccountRecordVo> list) {
        List<AccountRecordVo> collect = this.filterList(list, BizTypeEnum.BALL_COIN_EXCHANGE);
        // 发送红包总额
        statistics.setBallCoinExchangeAmount(this.totalAmount(collect));
    }


    /**
     * 红包数据处理
     *
     * @param statistics
     * @param list
     */
    private void handleRedPacket(AccountRecordStatistics statistics, List<AccountRecordVo> list) {
        List<AccountRecordVo> collect = this.filterList(list, BizTypeEnum.SEND_RED_PACKET);
        // 发送红包总额
        statistics.setSendRedPacketAmount(this.totalAmount(collect).negate());
        // 发送红包个数
        statistics.setSendRedPacketNum(collect.size());

        List<AccountRecordVo> expiredList = this.filterList(list, BizTypeEnum.EXPIRED_RED_PACKET);
        // 红包过期总额
        statistics.setExpiredRedPacketAmount(this.totalAmount(expiredList));
        // 红包过期个数
        statistics.setExpiredRedPacketNum(expiredList.size());

        List<AccountRecordVo> receivedList = this.filterList(list, BizTypeEnum.RECEIVED_RED_PACKET);
        // 收到红包总额
        statistics.setReceivedRedPacketAmount(this.totalAmount(receivedList));
        // 收到红包个数
        statistics.setReceivedRedPacketNum(receivedList.size());
    }


    /**
     * 投资数据处理
     *
     * @param statistics
     * @param list
     */
    private void handleInvest(AccountRecordStatistics statistics, List<AccountRecordVo> list) {
        List<AccountRecordVo> collect = this.filterList(list, BizTypeEnum.INVEST);
        // 投资笔数
        statistics.setInvestNum(collect.size());
        // 投资总额
        statistics.setInvestAmount(this.totalAmount(collect).negate());
        // 投资人数
        statistics.setInvestPeopleNum(this.peopleNum(collect));

        List<AccountRecordVo> investRevertList = this.filterList(list, BizTypeEnum.INVEST_REVERT);
        // 投资取消笔数
        statistics.setInvestRevertNum(investRevertList.size());
        // 投资取消总额
        statistics.setInvestRevertAmount(this.totalAmount(investRevertList));

        List<AccountRecordVo> investIncomeList = this.filterList(list, BizTypeEnum.INVEST_INCOME);
        // 投资本金和收益总额
        statistics.setInvestIncomeAmount(this.totalAmount(investIncomeList));
    }


    /**
     * 返佣处理
     *
     * @param statistics
     * @param list
     */
    private void handleSubRebate(AccountRecordStatistics statistics, List<AccountRecordVo> list) {
        List<AccountRecordVo> collect = this.filterList(list, BizTypeEnum.SUB_REBATE);
        // 返佣金额
        statistics.setSubRebateAmount(this.totalAmount(collect));
    }


    /**
     * 充值处理
     *
     * @param statistics
     * @param list
     */
    private void handleRecharge(AccountRecordStatistics statistics, List<AccountRecordVo> list) {
        // 充值
        List<AccountRecordVo> collect = this.filterList(list, BizTypeEnum.RECHARGE);
        // 充值笔数
        statistics.setRechargeNum(collect.size());
        // 充值总额
        statistics.setRechargeAmount(this.totalAmount(collect));
    }


    /**
     * 处理提现数据
     *
     * @param statistics
     * @param list
     */
    private void handleWithdraw(AccountRecordStatistics statistics, List<AccountRecordVo> list) {
        // 提现(提现类型流水记录中成功和失败都有记录)
        List<AccountRecordVo> collect = this.filterList(list, BizTypeEnum.WITHDRAW);
        // 提现笔数
        statistics.setWithdrawNum(collect.size());
        // 提现总额
        statistics.setWithdrawAmount(this.totalAmount(collect).negate());

        // 提现撤销
        List<AccountRecordVo> rejectList = this.filterList(list, BizTypeEnum.WITHDRAW_REJECT);
        // 提现撤销总额
        statistics.setWithdrawRejectNum(rejectList.size());
        // 提现撤销笔数
        statistics.setWithdrawRejectAmount(this.totalAmount(rejectList));
    }


    /**
     * 筛选不同业务类型
     *
     * @return
     */
    private List<AccountRecordVo> filterList(List<AccountRecordVo> list, BizTypeEnum typeEnum) {
        return list.stream()
                .filter(record -> typeEnum.equals(record.getBizType()))
                .collect(Collectors.toList());
    }


    /**
     * 统计人数
     *
     * @param list
     * @return
     */
    private long peopleNum(List<AccountRecordVo> list) {
        return list.stream()
                .map(AccountRecordVo::getUserId)
                .distinct()
                .count();
    }


    /**
     * 统计总额
     *
     * @param list
     * @return
     */
    private BigDecimal totalAmount(List<AccountRecordVo> list) {
        BigDecimal amount = BigDecimal.ZERO;
        if (list.size() > 0) {
            amount = list.stream()
                    .map(recordVo -> recordVo.getAmount().multiply(recordVo.getRate()))
                    .reduce(BigDecimal::add).get();
        }
        return amount.setScale(2, RoundingMode.HALF_UP);
    }


    @Override
    public Pagination listPage(String searchKeyword, String[] searchDate, String pageOrderName, String pageOrderBy, int pageNumber, int pageSize) {
        Sql sql = Sqls.create("SELECT s.* FROM account_record_statistics AS s $condition");
        Cnd cnd = Cnd.where("s.delFlag", "=", 0);
        if (Strings.isNotBlank(searchKeyword)) {
            // 目前只有按日期搜索，没有按关键字搜索，以后补充
        }
        if (searchDate != null && searchDate.length == 2) {
            cnd.and("s.date", ">=", searchDate[0]);
            cnd.and("s.date", "<=", searchDate[1]);
        }
        String sort;
        if (Strings.isNotBlank(pageOrderName) && Strings.isNotBlank(pageOrderBy)) {
            sort = "ORDER BY s." + pageOrderName + " " + pageOrderBy;
        } else {
            sort = "ORDER BY s.date DESC";
        }
        sql.setCondition(cnd);
        sql.appendSourceSql(sort);
        return dbService.listPage(AccountRecordStatistics.class, pageNumber, pageSize, sql.toString());
    }

}
